#! /usr/bin/env python
# -*- coding: utf-8 -*-

import time
import datetime
import threading
import binascii
import platform
import logging

import SerialTool
import SerialHelper

if platform.system() == "Windows":
    from  serial.tools import list_ports
elif platform.system() == "Linux":
    import glob, os, re

import tkinter as tk
from tkinter import ttk

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S')

class MainSerialToolUI(SerialTool.SerialToolUI):

    def __init__(self, master=None):
        super().__init__()
        self.ser = None
        self.receive_count = 0
        self.receive_data = ""
        self.list_box_serial = list()
        self.find_all_serial()

        self.first_time_on = 'yes'

    def __del__(self):
        if platform.system() == "Linux":
            try:
                self.ser.SetStopEvent()
            except:
                pass

    def find_all_serial(self):
        '''
        获取到串口列表
        '''
        if platform.system() == "Windows":
            try:
                self.temp_serial = list()
                for com in list_ports.comports():
                    #strCom = com[0] + ": " + com[1][:-7].decode("gbk").encode("utf-8")
                    strCom = com[0]
                    self.temp_serial.append(strCom)
                for item in self.temp_serial:
                    if item not in self.list_box_serial:
                        self.frm_left_listbox.insert("end", item)
                for item in self.list_box_serial:
                    if item not in self.temp_serial:
                        index = list(self.frm_left_listbox.get(0, self.frm_left_listbox.size())).index(item)
                        self.frm_left_listbox.delete(index)

                self.list_box_serial = self.temp_serial

                self.thread_findserial = threading.Timer(1, self.find_all_serial)
                self.thread_findserial.setDaemon(True)
                self.thread_findserial.start()
            except Exception as e:
                logging.error(e)
        elif platform.system() == "Linux":
            try:
                self.temp_serial = list()
                self.temp_serial = self.find_usb_tty()
                for item in self.temp_serial:
                    if item not in self.list_box_serial:
                        self.frm_left_listbox.insert("end", item)
                for item in self.list_box_serial:
                    if item not in self.temp_serial:
                        index = list(self.frm_left_listbox.get(0, self.frm_left_listbox.size())).index(item)
                        self.frm_left_listbox.delete(index)
                self.list_box_serial = self.temp_serial

                self.thread_findserial = threading.Timer(1, self.find_all_serial)
                self.thread_findserial.setDaemon(True)
                self.thread_findserial.start()
            except Exception as e:
                logging.error(e)

    def ClosePort(self):
        '''
        Close com port
        '''
        try:
            self.ser.stop()
            self.receive_count = 0
        except Exception as e:
            logging.error(e)
        self.frm_left_open_btn["text"] = "Open"
        self.frm_left_open_btn["bg"] = "#008B8B"
        self.frm_status_label["text"] = "Close Serial Successful!"
        self.frm_status_label["fg"] = "#8DEEEE"       

    def OpenPort(self):
        '''
        Open com port
        '''
        try:
            self.currentStrCom = self.frm_left_listbox.get(self.frm_left_listbox.curselection())
            if platform.system() == "Windows":
                self.port = self.currentStrCom.split(":")[0]
            elif platform.system() == "Linux":
                self.port = self.currentStrCom
            self.baudrate = self.frm_left_combobox_baudrate.get()
            self.parity = self.frm_left_combobox_parity.get()
            self.databit = self.frm_left_combobox_databit.get()
            self.stopbit = self.frm_left_combobox_stopbit.get()
            self.ser = SerialHelper.SerialHelper(Port=self.port,
                                                    BaudRate=self.baudrate,
                                                    ByteSize=self.databit,
                                                    Parity=self.parity,
                                                    Stopbits=self.stopbit)
            self.ser.start()
            if self.ser.alive:
                self.frm_status_label["text"] = "Open [{0}] Successful!".format(self.currentStrCom)
                self.frm_status_label["fg"] = "#66CD00"
                self.frm_left_open_btn["text"] = "Close"
                self.frm_left_open_btn["bg"] = "#F08080"

                self.thread_read = threading.Thread(target=self.SerialRead)
                self.thread_read.setDaemon(True)
                self.thread_read.start()

        except Exception as e:
            logging.error(e)
            try:
                self.frm_status_label["text"] = "Open [{0}] Failed!".format(self.currentStrCom)
                self.frm_status_label["fg"] = "#DC143C"
            except Exception as ex:
                logging.error(ex)

    def BtnOpenCb(self, event=None):
        '''
        双击列表打开/关闭串口
        '''
        if self.frm_left_open_btn["text"] == "Open":
            self.OpenPort()
        else:
            self.ClosePort()

        #self.Toggle()

    def Clear(self):
        self.frm_right_receive.delete("0.0", "end")
        self.receive_count = 0

    def Send(self):
        '''
        向已打开的串口发送数据
        如果为Hex发送，示例："31 32 33" [即为字符串 "123"]
        '''
        if self.ser:
            try:
                # 发送新行
                if self.new_line_cbtn_var.get() == 0:
                    send_data = str(self.frm_right_send.get("0.0", "end").encode("gbk")).strip()
                else:
                    send_data = str(self.frm_right_send.get("0.0", "end")).strip() + "\r\n"  
                
                # 是否十六进制发送
                if self.send_hex_cbtn_var.get() == 1:
                    self.ser.write(send_data, isHex=True)
                else:
                    self.ser.write(send_data.encode('utf-8'))
            except Exception as e:
                self.frm_right_receive.insert("end", str(e) + "\n")
                logging.error(e)

    def SerialRead(self):
        '''
        线程读取串口发送的数据
        '''

        received_flag = 0
        data_str_temp = ''
        data_str = ''
        total_receive_length = 0

        #while self.ser.alive:
        while self.ser.l_serial.isOpen():
            try:
                self.current_time_str.set(str(time.ctime()))
                n = self.ser.l_serial.inWaiting()
                if n > 0:
                    received_flag = 1
                    self.receive_data = self.ser.l_serial.read_all()

                    #parse the data
                    data_str_temp = self.receive_data.decode('utf-8')
                    data_str = data_str + data_str_temp

                    # 接收显示是否为Hex
                    if self.receive_hex_checkbox_var.get() == 1:
                        self.receive_data = self.space_b2a_hex(self.receive_data)
                    #self.frm_right_receive.insert("end", "[" + str(datetime.datetime.now()) + " - "
                    #                                + str(self.receive_count) + "]:\n", "green")
                    self.frm_right_receive.insert("end", self.receive_data)
                    self.frm_right_receive.see("end")
                    total_receive_length += n
                    if total_receive_length > self.thresholdValue:
                        total_receive_length = 0
                        self.Clear()

                    self.receive_data = ""
                ######################################################################################

                elif received_flag == 1:
                    received_flag = 0

                    if len(data_str) > 200:
                        vlist = data_str.split(',')
                        if vlist[0] == ' IV':
                            if self.log_to_file_checkbox_var.get() == 1:
                                with open(fileName,'a+') as fp:
                                    if self.first_time_on == 'yes':
                                        self.first_time_on = 'no'
                                        fp.write('time, ')
                                        vlist2 = vlist[0::2]
                                        vlist2.pop()
                                        for x in vlist2:
                                            s1 = x + ','
                                            fp.write(s1)
                                        fp.write('\n')

                                    fp.write(time.ctime() + ', ')
                                    vlist2 = vlist[1::2]
                                    for x in vlist2:
                                        s1 = x + ','
                                        fp.write(s1)
                                    fp.write('\n')

                            vdict = dict(zip(vlist[::2], vlist[1::2]))                     
                            try:
                                self.input_voltage_str.set(vdict[' IV'])
                                self.bat_voltage_str.set(vdict[' BV'])
                                self.bat_current_str.set(vdict[' BI'])
                                self.bat_state_str.set(vdict[' B_MNR'])
                                self.ac_state_str.set(vdict[' AC_MNR'])
                                self.mainten_state_str.set(vdict[' MAINT'])
                                self.hours_since_last_mainten_str.set(vdict[' hs_last'])
                                self.total_run_hours_str.set(vdict[' t_run_hs'])
                                self.total_ac_off_count_str.set(vdict[' ac_off_cnt'])
                                self.sn_str.set(vdict[' sn'])
                                self.hw_version_str.set(vdict[' hw'])
                                self.sw_version_str.set(vdict[' sw'])
                                self.charge_pwm_str.set(vdict[' PWM'])
                                self.max_charge_hours_str.set(vdict[' max'])
                                self.min_charge_hours_str.set(vdict[' min'])
                                self.avg_charge_hours_str.set(vdict[' avg'])
                                self.current_charge_hours_str.set(vdict[' cur'])
                                self.total_charge_hours_str.set(vdict[' t_hs'])
                                self.total_charge_count_str.set(vdict[' t_cnt'])
                                self.motor_state_str.set(vdict[' mc_state'])
                                self.motor_current_str.set(vdict[' mc_cur'])
                            except KeyError:
                                #print(' data_str' + data_str)
                                #print(' vdict' + vdict)
                                pass
                        data_str = '' 
                else:
                    time.sleep(0.05)
                ######################################################################################
            except Exception as e:
                logging.error(e)
                self.receive_data = ""
                #self.ClosePort()
                #self.ser.stop()
                #self.ser = None
                #break

    def find_usb_tty(self, vendor_id=None, product_id=None):
        '''
        发现串口设备
        '''
        tty_devs = list()
        for dn in glob.glob('/sys/bus/usb/devices/*') :
            try:
                vid = int(open(os.path.join(dn, "idVendor" )).read().strip(), 16)
                pid = int(open(os.path.join(dn, "idProduct")).read().strip(), 16)
                if  ((vendor_id is None) or (vid == vendor_id)) and ((product_id is None) or (pid == product_id)) :
                    dns = glob.glob(os.path.join(dn, os.path.basename(dn) + "*"))
                    for sdn in dns :
                        for fn in glob.glob(os.path.join(sdn, "*")) :
                            if  re.search(r"\/ttyUSB[0-9]+$", fn) :
                                tty_devs.append(os.path.join("/dev", os.path.basename(fn)))
            except Exception as ex:
                pass
        return tty_devs

    def space_b2a_hex(self, data):
        '''
        格式化接收到的数据字符串
        示例：123 --> 31 32 33
        '''
        new_data_list = list()
        new_data = ""

        hex_data = binascii.b2a_hex(data)
        temp_data = ""
        for index,value in enumerate(hex_data): 
            temp_data += str(value)
            if len(temp_data) == 2:
                new_data_list.append(temp_data)
                temp_data = ""
        for index,value in enumerate(new_data_list):
            if index%25 == 0 and index != 0:
                new_data += "\n"
            new_data += str(value)
            new_data += " "

        return new_data

if __name__ == '__main__':
    '''
    main loop
    '''
    time_string = str(time.time())
    time_string = time_string.replace('.','_',1)
    fileName = 'Log_'+time_string+'.txt'

    root = tk.Tk()
    root.title("ATA0001P04 TEST TOOL")
    if SerialTool.g_default_theme == "dark":
        root.configure(bg="#292929")
        combostyle = ttk.Style()
        combostyle.theme_use('alt')
        combostyle.configure("TCombobox", selectbackground="#292929", fieldbackground="#292929",
                                          background="#292929", foreground="#FFFFFF")
    MainSerialToolUI(master=root)
    root.resizable(False, False)
    root.mainloop()